package cn.agiledata.bank.transaction.logic;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;

import cn.agiledata.bank.common.util.DateUtil;
import cn.agiledata.bank.common.util.StringUtil;
import cn.agiledata.bank.transaction.exception.TransactionException;

/**
 * 通过FTP协议加载批量导入文件的管理器
 * 
 * @author maxianbo
 * 
 */
public class FtpBatchImportFileManager implements BatchImportFileManager {
    private String host;

    private int port;

    private String username;

    private String password;

    private String importRoot;

    private String historyRoot;

    private String encoding;

    private boolean log;

    private Logger logger = Logger.getLogger(FtpBatchImportFileManager.class);

    public void setHost(String server) {
        this.host = server;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setImportRoot(String importRoot) {
        this.importRoot = importRoot;
    }

    public void setHistoryRoot(String historyRoot) {
        this.historyRoot = historyRoot;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    private void info(Object o) {
        if (o instanceof Exception) {
            Exception e = (Exception) o;
            if (this.log) {
                this.logger.info("", e);
            } else {
                e.printStackTrace(System.out);
            }
        } else {
            if (this.log) {
                this.logger.info(o);
            } else {
                System.out.println(o);
            }
        }
    }

    private FTPClient createConnection() throws IOException {
        FTPClient ftp = new FTPClient();
        /*
        info("Connecting to FTP server " + this.host + " ...");
        if (this.port <= 0) {
            ftp.connect(this.host);
        } else {
            ftp.connect(this.host, this.port);
        }
        ftp.login(this.username, this.password);
        if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
            info("FTP server refused connection.");
            ftp.disconnect();
            return null;
        }
        if (this.encoding != null & this.encoding.length() > 0) {
            ftp.setControlEncoding(this.encoding); // 编码格式
        }
        ftp.setFileType(FTP.BINARY_FILE_TYPE); // 二进制文件类型
        ftp.enterLocalPassiveMode(); // 被动模式
        info("FTP server has been connected successfully.");*/
        return ftp;
    }

    private FTPClient createImportConnection() throws IOException {
        FTPClient ftp = createConnection();
        if (ftp != null && this.importRoot != null
                && this.importRoot.length() > 0) {
            ftp.changeWorkingDirectory(this.importRoot);
            info("Cd to " + this.importRoot);
        }
        return ftp;
    }

    private FTPClient createHistoryConnection() throws IOException {
        FTPClient ftp = createConnection();
        if (ftp != null && this.historyRoot != null
                && this.historyRoot.length() > 0) {
            ftp.changeWorkingDirectory(this.historyRoot);
            info("Cd to " + this.historyRoot);
        }
        return ftp;
    }

    private void destroyConnection(FTPClient ftp) throws IOException {
        ftp.logout();
        info("Logouted from FTP server.");
        ftp.disconnect();
        info("Disconnected from FTP server.");
    }

    private boolean isValidDirectory(FTPFile file) {
        String name = file.getName();
        return file.isDirectory() && StringUtil.isNumber(name);
    }

    private boolean isValidFile(FTPFile file) {
        String name = file.getName();
        return file.isFile() && name.toLowerCase().endsWith("txt");
    }

    public Date[] loadCreateTimes(String accountId) throws TransactionException {
        try {
            FTPClient ftp = createImportConnection();
            if (ftp == null) {
                return new Date[0];
            }
            List list = new ArrayList();
            if (FTPReply.isPositiveCompletion(ftp.cwd(accountId))) {
                FTPFile[] files = ftp.listFiles();
                for (int i = 0; i < files.length; i++) {
                    if (!isValidFile(files[i])) {
                        continue;
                    }
                    InputStream in = ftp.retrieveFileStream(files[i].getName());
                    if (in == null) {
                        continue;
                    }
                    try {
                        BatchImportFile bif = new BatchImportFile();
                        bif.loadTitleData(in); // 只加载文件头数据即可
                        if (accountId.equals(bif.getAccountId())) {
                            list.add(bif.getCreateTime());
                            info("Loaded create time that is "
                                    + DateUtil.date2String(bif.getCreateTime(),
                                            "yyyy-MM-dd HH:mm:ss") + ".");
                        }
                    } catch (TransactionException e) {
                        info(e);
                        info("Next continue ...");
                    }
                    in.close();
                    ftp.completePendingCommand();
                }
            } else {
                info("AccountId " + accountId + " is not existed. To exit ...");
            }
            destroyConnection(ftp);
            return (Date[]) list.toArray(new Date[0]);
        } catch (IOException e) {
            throw TransactionException.getImportFileIOException(e);
        }
    }

    private BatchImportFile loadBatchImportFile(FTPClient ftp, String fileName,
            String accountId, String date, boolean loadTransData)
            throws IOException, TransactionException {
        BatchImportFile file = null;
        InputStream in = ftp.retrieveFileStream(fileName);
        if (in != null) {
            file = new BatchImportFile();
            info("Loading " + accountId + "/" + fileName + " ...");
            file.loadTitleData(in); // 先加载文件头数据进行判断
            String fileDate = DateUtil.date2String(file.getCreateTime(),
                    "yyyyMMdd");
            if (accountId.equals(file.getAccountId()) && date.equals(fileDate)) {
                if (loadTransData) {
                    file.loadTransData(in);
                }
            } else {
                info("Date of " + accountId + "/" + fileName + " is "
                        + fileDate + ", which is not the specified date.");
                file = null;
            }
            in.close();
            ftp.completePendingCommand();
        }
        return file;
    }

    public BatchImportFile loadBatchImportFile(String accountId, Date createTime)
            throws TransactionException {
        try {
            FTPClient ftp = createImportConnection();
            if (ftp == null) {
                return null;
            }
            BatchImportFile file = null;
            if (FTPReply.isPositiveCompletion(ftp.cwd(accountId))) {
                String date = DateUtil.date2String(createTime, "yyyyMMdd");
                String fileName = date + ".txt";
                file = loadBatchImportFile(ftp, fileName, accountId, date, true);
                if (file == null) {
                    info(accountId + "/" + fileName
                            + " is not existed. To load all valid files.");
                    FTPFile[] files = ftp.listFiles();
                    for (int i = 0; i < files.length; i++) {
                        if (!isValidFile(files[i])) {
                            continue;
                        }
                        try {
                            file = loadBatchImportFile(ftp, files[i].getName(),
                                    accountId, date, true);
                            if (file != null) {
                                break;
                            }
                        } catch (TransactionException e) {
                            info(e);
                            info("Next continue ...");
                        }
                    }
                }
            }
            destroyConnection(ftp);
            return file;
        } catch (IOException e) {
            throw TransactionException.getImportFileIOException(e);
        }
    }

    public String[] loadAllAccountIds() throws TransactionException {
        try {
            FTPClient ftp = createImportConnection();
            if (ftp == null) {
                return new String[0];
            }
            List list = new ArrayList();
            FTPFile[] merchants = ftp.listFiles();
            for (int i = 0; i < merchants.length; i++) {
                if (isValidDirectory(merchants[i])) {
                    list.add(merchants[i].getName());
                    info("Found accountId " + merchants[i].getName());
                }
            }
            destroyConnection(ftp);
            return (String[]) list.toArray(new String[0]);
        } catch (IOException e) {
            throw TransactionException.getImportFileIOException(e);
        }
    }

    public BatchImportFile[] loadBatchImportFilesByAccountId(String accountId)
            throws TransactionException {
        try {
            FTPClient ftp = createImportConnection();
            if (ftp == null) {
                return new BatchImportFile[0];
            }
            List list = new ArrayList();
            if (FTPReply.isPositiveCompletion(ftp.cwd(accountId))) {
                FTPFile[] files = ftp.listFiles();
                for (int j = 0; j < files.length; j++) {
                    if (!isValidFile(files[j])) {
                        continue;
                    }
                    info("Loading " + accountId + "/" + files[j].getName()
                            + " ...");
                    InputStream in = ftp.retrieveFileStream(files[j].getName());
                    if (in == null) {
                        continue;
                    }
                    try {
                        BatchImportFile bif = BatchImportFile.getInstance(in);
                        list.add(bif);
                    } catch (TransactionException e) {
                        info(e);
                        info("Next continue ...");
                    }
                    in.close();
                    ftp.completePendingCommand();
                }
                ftp.cdup();
            }
            destroyConnection(ftp);
            return (BatchImportFile[]) list.toArray(new BatchImportFile[0]);
        } catch (IOException e) {
            throw TransactionException.getImportFileIOException(e);
        }
    }

    public BatchImportFile[] loadAllBatchImportFiles()
            throws TransactionException {
        try {
            FTPClient ftp = createImportConnection();
            if (ftp == null) {
                return new BatchImportFile[0];
            }
            List list = new ArrayList();
            FTPFile[] merchants = ftp.listFiles();
            for (int i = 0; i < merchants.length; i++) {
                if (!isValidDirectory(merchants[i])) {
                    continue;
                }
                String merchantId = merchants[i].getName();
                ftp.cwd(merchantId);
                FTPFile[] files = ftp.listFiles();
                for (int j = 0; j < files.length; j++) {
                    if (!isValidFile(files[j])) {
                        continue;
                    }
                    info("Loading " + merchantId + "/" + files[j].getName()
                            + " ...");
                    InputStream in = ftp.retrieveFileStream(files[j].getName());
                    if (in == null) {
                        continue;
                    }
                    try {
                        BatchImportFile bif = BatchImportFile.getInstance(in);
                        list.add(bif);
                    } catch (TransactionException e) {
                        info(e);
                        info("Next continue ...");
                    }
                    in.close();
                    ftp.completePendingCommand();
                }
                ftp.cdup();
            }
            destroyConnection(ftp);
            return (BatchImportFile[]) list.toArray(new BatchImportFile[0]);
        } catch (IOException e) {
            throw TransactionException.getImportFileIOException(e);
        }
    }

    public void backupBatchImportFile(String accountId, Date createTime) {
        try {
            FTPClient ftp = createImportConnection();
            if (ftp == null) {
                return;
            }
            if (FTPReply.isPositiveCompletion(ftp.cwd(accountId))) {
                String date = DateUtil.date2String(createTime, "yyyyMMdd");
                String fileName = date + ".txt";
                BatchImportFile file = null;
                try {
                    file = loadBatchImportFile(ftp, fileName, accountId, date,
                            false);
                } catch (TransactionException e) {
                }
                if (file == null) {
                    info("Be fail in loading " + accountId + "/" + fileName
                            + ". To load all valid files.");
                    FTPFile[] files = ftp.listFiles();
                    for (int i = 0; i < files.length; i++) {
                        if (!isValidFile(files[i])) {
                            continue;
                        }
                        try {
                            file = loadBatchImportFile(ftp, files[i].getName(),
                                    accountId, date, false);
                            if (file != null) {
                                fileName = files[i].getName();
                                break;
                            }
                        } catch (TransactionException e) {
                            info(e);
                            info("Next continue ...");
                        }
                    }
                }
                if (file != null) { // 此时的file不为空则说明已找到相应的批量导入文件
                    InputStream in = ftp.retrieveFileStream(fileName);
                    ftp.completePendingCommand();
                    FTPClient hftp = createHistoryConnection();
                    if (!FTPReply.isPositiveCompletion(hftp.cwd(accountId))) {
                        hftp.mkd(accountId);
                        hftp.cwd(accountId);
                    }
                    info("Copying " + accountId + "/" + fileName
                            + " to history ....");
                    hftp.storeFile(fileName, in);
                    info("Copy has finished.");
                    in.close();
                    destroyConnection(hftp);
                    info("Deleting " + accountId + "/" + fileName);
                    ftp.deleteFile(fileName);
                    info("Deletion has finished.");
                }
            }
            destroyConnection(ftp);
        } catch (IOException e) {
            info("An IOException occured: " + e.getMessage());
        }
    }

/*    public static void main(String[] args) {
        FtpBatchImportFileManager manager = new FtpBatchImportFileManager();
        manager.host = "192.168.1.118";
        manager.importRoot = "batch/import";
        manager.historyRoot = "batch/history";
        manager.username = "test1";
        manager.password = "123456";
        manager.log = false;
        // Date date = DateUtil.string2Date("20071212", "yyyyMMdd");
        try {
            manager.loadAllAccountIds();
        } catch (TransactionException e) {
            e.printStackTrace();
        }
    }*/
}
